using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;
using System.IO;

namespace Av.Utils
{

    public class Common
    {
        #region Hex Encode/Decode
        private static char[] HX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

        /// <summary>
        /// Encode hex
        /// </summary>
        /// <param name="ba">ByteArray to encode</param>
        /// <returns>Hex encoded data</returns>
        public static string EncodeHex(byte[] ba)
        {
            if(ba == null) return null;
            char[] ca = new char[ba.Length << 1];
            for(int ck = 0; ck < ba.Length; ck++)
            {
                byte b = ba[ck];
                ca[(ck << 1)] = HX[b >> 4];
                ca[(ck << 1) + 1] = HX[b & 0x0F];
            }
            return new string(ca);
        }

        public static string EncodeHex(byte[] ba, int offset, int length)
        {
            if(ba == null) return null;
            char[] ca = new char[length << 1];
            //char[] ca = new char[ba.Length << 1];
            //for (int ck = offset; ck < offset + length; ck++)
            for(int ck = 0; ck < length; ck++)
            {
                byte b = ba[ck + offset];
                ca[(ck << 1)] = HX[b >> 4];
                ca[(ck << 1) + 1] = HX[b & 0x0F];
            }
            return new string(ca);
        }
        /// <summary>
        /// Decode hex
        /// </summary>
        /// <param name="s">Hex encoded data</param>
        /// <returns>Decoded ByteArray</returns>
        public static byte[] DecodeHex(string s)
        {
            if(s == null) return null;
            byte[] ba = new byte[s.Length >> 1];
            int ck = 0;
            for(int i = 0; i < ba.Length; i++)
            {
                switch(s[ck++])
                {
                    case '0': break;
                    case '1': ba[i] = 0x10; break;
                    case '2': ba[i] = 0x20; break;
                    case '3': ba[i] = 0x30; break;
                    case '4': ba[i] = 0x40; break;
                    case '5': ba[i] = 0x50; break;
                    case '6': ba[i] = 0x60; break;
                    case '7': ba[i] = 0x70; break;
                    case '8': ba[i] = 0x80; break;
                    case '9': ba[i] = 0x90; break;
                    case 'A':
                    case 'a': ba[i] = 0xA0; break;
                    case 'B':
                    case 'b': ba[i] = 0xB0; break;
                    case 'C':
                    case 'c': ba[i] = 0xC0; break;
                    case 'D':
                    case 'd': ba[i] = 0xD0; break;
                    case 'E':
                    case 'e': ba[i] = 0xE0; break;
                    case 'F':
                    case 'f': ba[i] = 0xF0; break;
                    default: throw new ArgumentException("String is not hex encoded data.");
                }
                switch(s[ck++])
                {
                    case '0': break;
                    case '1': ba[i] |= 0x01; break;
                    case '2': ba[i] |= 0x02; break;
                    case '3': ba[i] |= 0x03; break;
                    case '4': ba[i] |= 0x04; break;
                    case '5': ba[i] |= 0x05; break;
                    case '6': ba[i] |= 0x06; break;
                    case '7': ba[i] |= 0x07; break;
                    case '8': ba[i] |= 0x08; break;
                    case '9': ba[i] |= 0x09; break;
                    case 'A':
                    case 'a': ba[i] |= 0x0A; break;
                    case 'B':
                    case 'b': ba[i] |= 0x0B; break;
                    case 'C':
                    case 'c': ba[i] |= 0x0C; break;
                    case 'D':
                    case 'd': ba[i] |= 0x0D; break;
                    case 'E':
                    case 'e': ba[i] |= 0x0E; break;
                    case 'F':
                    case 'f': ba[i] |= 0x0F; break;
                    default: throw new ArgumentException("String is not hex encoded data.");
                }
            }
            return ba;
        }
        #endregion

        /// <summary>
        /// Returns value in string, folowed underscore, i.e. "element_id_37"
        /// </summary>
        /// <param name="s">String containing ID, i.e. "btn_nr3"</param>
        public static string GetLastTokenAfterUnderscore(string s)
        {
            string id = string.Empty;

            int pos = s.LastIndexOf('_');
            if( pos > 0 )
            {
                id = s.Substring(pos + 1);
            }

            return id;
        }

        /// <summary>
        /// Removes any spaces or zerroes from beggining of IP, i.e. "192.168.048.011" -> "192.168.48.11"
        /// </summary>
        /// <param name="sIp">Dotted IP address</param>
        /// <returns></returns>
        public static string NormalizeIp(string sIp)
        {
            string[] arIp = sIp.Split(new char[] { '.' });
            if(arIp != null)
            {
                sIp = "";
                // Remove all spaces and zerroes
                for(int i = 0; i < arIp.Length; i++)
                    arIp[i] = arIp[i].TrimStart(new char[] { '0', ' ' });

                sIp = string.Join(".", arIp);
            }

            return sIp;
        }

        /// <summary>
        /// Gets path of executable and ensures its ends w/ '\'
        /// </summary>
        public static string GetPath()
        {
            string aPath = "";
            try
            {
                System.Reflection.Module[] modules = System.Reflection.Assembly.GetExecutingAssembly().GetModules();
                aPath = System.IO.Path.GetDirectoryName(modules[0].FullyQualifiedName);
                if((aPath != "") && (aPath[aPath.Length - 1] != '\\')) aPath += '\\';
            } catch(Exception) { }

            return aPath;
        }

        /// <summary>
        /// Replace and returns '{' w/ '{{' and '}' w/ '}}'
        /// </summary>
        /// <returns></returns>
        public static string EscapeBraces(string str)
        {
            if(!string.IsNullOrEmpty(str))
            {
                str = str.Replace("{", "{{");
                str = str.Replace("}", "}}");
            }

            return str;
        }

    }

    #region Extended memory stream


    /// <summary>
    /// Memory stream with extra methods for easer ISO8583 document creation
    /// </summary>
    public class MemoryStreamISO8583Ex : MemoryStreamEx
    {
        private long _bitmapoffset = 0;
        private int _bitmaplength = 0;
        private bool _bitmapinhex = false;

        #region Bitmap writing

        /// <summary>
        /// Writes ISO8583 bitmap at a current possition of memory stream.
        /// </summary>
        /// <param name="length">Bitmap length in bits (64 or 128 bits only)</param>
        public void WriteISO8583BitMap(int length)
        {
            WriteISO8583BitMap(length, false);
        }
        /// <summary>
        /// Writes ISO8583 bitmap at current possition and will be updated as new ISO8583 fields will be added.
        /// </summary>
        /// <param name="length">Bitmap length. Valid values(64/128)</param>
        /// <param name="HexEncoded">True if bitmap should be represented as hex encoded (used for ASCII formated documents)</param>
        public void WriteISO8583BitMap(int length, bool HexEncoded)
        {
            if(length != 64 && length != 128) throw new Exception("Incorrect bitmap length");
            if(_bitmaplength != 0) throw new Exception("Bitmap already mapped");
            _bitmapoffset = Position;
            _bitmaplength = length;
            _bitmapinhex = HexEncoded;
            if(!_bitmapinhex)
            {
                byte[] ba = new byte[length >> 3];
                if(length == 128) ba[0] = 128;
                base.Write(ba, 0, ba.Length);
            } else
            {
                byte[] ba = new byte[length >> 2];
                for(int i = 0; i < ba.Length; i++) ba[i] = 48;
                if(length == 128) ba[0] = (byte)'8';
                base.Write(ba, 0, ba.Length);
            }
        }
        private static char[] HX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        private void SetBit(int bit)
        {
            if(bit > _bitmaplength || bit < 1) throw new Exception("Bitmap index out of bounds");
            long ps = Position;
            try
            {
                bit--;
                if(!_bitmapinhex)
                {
                    int ofs = (bit >> 3);
                    Seek(_bitmapoffset + ofs, SeekOrigin.Begin);
                    byte b = (byte)((128 >> (bit & 7)) | ReadByte());
                    Seek(_bitmapoffset + ofs, SeekOrigin.Begin);
                    WriteByte(b);
                } else
                {
                    int ofs = (bit >> 2);
                    Seek(_bitmapoffset + ofs, SeekOrigin.Begin);
                    char c = (char)ReadByte();
                    Seek(_bitmapoffset + ofs, SeekOrigin.Begin);
                    byte b = (byte)((8 >> (bit & 3)));
                    switch(c)
                    {
                        case '0': b |= 0x0; break;
                        case '1': b |= 0x1; break;
                        case '2': b |= 0x2; break;
                        case '3': b |= 0x3; break;
                        case '4': b |= 0x4; break;
                        case '5': b |= 0x5; break;
                        case '6': b |= 0x6; break;
                        case '7': b |= 0x7; break;
                        case '8': b |= 0x8; break;
                        case '9': b |= 0x9; break;
                        case 'A': b |= 0xA; break;
                        case 'B': b |= 0xB; break;
                        case 'C': b |= 0xC; break;
                        case 'D': b |= 0xD; break;
                        case 'E': b |= 0xE; break;
                        case 'F': b |= 0xF; break;
                    }
                    WriteByte((byte)HX[b]);
                }
            } finally { Seek(ps, SeekOrigin.Begin); }
        }

        /// <summary>
        /// Write element
        /// </summary>
        /// <param name="Length">Length</param>
        /// <param name="buffer">Buffer</param>
        /// <param name="bit">Field number</param>
        public void Write(string Length, byte[] buffer, int bit)
        {
            base.WriteString(Length);
            base.Write(buffer, 0, buffer.Length);
            SetBit(bit);
        }

        /// <summary>
        /// Write element
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="offset">Offset</param>
        /// <param name="count">Count</param>
        /// <param name="bit">Field number</param>
        public void Write(byte[] buffer, int offset, int count, int bit)
        {
            base.Write(buffer, offset, count);
            SetBit(bit);
        }
        /// <summary>
        /// Write element
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="bit">Field number</param>
        public void Write(byte[] buffer, int bit)
        {
            base.Write(buffer);
            SetBit(bit);
        }
        /// <summary>
        /// Write string as BCD encoded data
        /// </summary>
        /// <param name="s">Value</param>
        /// <param name="bit">Field number</param>
        public void WriteBcdEncoded(string s, int bit)
        {
            base.WriteBcdEncoded(s);
            SetBit(bit);
        }
        /// <summary>
        /// Write string 
        /// </summary>
        /// <param name="s">Value</param>
        /// <param name="bit">Field number</param>
        public void WriteString(string s, int bit)
        {
            base.WriteString(s);
            SetBit(bit);
        }
        /// <summary>
        /// Write buffer data as HEX encoded string (used for ASCII encoded ISO8583 documents)
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="bit">Field number</param>
        public void WriteHexEncoded(byte[] buffer, int bit)
        {
            base.WriteHexEncoded(buffer);
            SetBit(bit);
        }
        /// <summary>
        /// Write byte
        /// </summary>
        /// <param name="value">Value</param>
        /// <param name="bit">Field number</param>
        public void WriteByte(byte value, int bit)
        {
            base.WriteByte(value);
            SetBit(bit);
        }

        #endregion

        /// <summary>
        /// Basic constructor
        /// </summary>
        public MemoryStreamISO8583Ex() : base() { }

    }

    /// <summary>
    /// Memory stream extended
    /// </summary>
    public class MemoryStreamEx : MemoryStream
    {

        #region Reader
        /// <summary>
        /// Skips number of bytes in a given memory stream
        /// </summary>
        /// <param name="count">Number of bytes to skipp</param>
        public long Skip(int count)
        {

            long ps = Position;
            return Seek(count, SeekOrigin.Current) - ps;
        }

        /// <summary>
        /// Returns the index of next occurence of byte b in the stream starting at current position
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public long FindNext(byte b)
        {
            byte[] ba = GetBuffer();
            return Array.IndexOf<byte>(ba, b, (int)Position);
        }

        /// <summary>
        /// Skips the stream until the given byte is found. If byte is not found skips to the end of stream
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public long SkipTo(byte b)
        {
            long rez = FindNext(b);
            return rez == -1 ? Seek(0, SeekOrigin.End) : Seek(rez, SeekOrigin.Begin);
        }

        /// <summary>
        /// Reads the stream until given byte is found. If byte is not found reads to the end of stream
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public byte[] ReadTo(byte b)
        {
            long rez = FindNext(b);
            byte[] ba = new byte[rez - Position];
            Read(ba);
            return ba;
        }

        public string ReadStringTo(byte b, Encoding encoding)
        {
            if(encoding == null)
                throw new ArgumentNullException("Encoding cannot be null");
            return encoding.GetString(ReadTo(b));
        }

        /// <summary>
        /// Reads number of bytes in a given memory stream
        /// </summary>
        /// <param name="count">Number of bytes to read</param>
        /// <returns>Bytes read</returns>
        public byte[] Read(int count)
        {
            byte[] ba = new byte[count];
            int i = Read(ba, 0, count);
            if(i != count)
            {
                byte[] bb = new byte[i];
                Buffer.BlockCopy(ba, 0, bb, 0, i);
                return bb;
            }
            return ba;
        }

        /// <summary>
        /// Reads HEX encoded byte array.
        /// </summary>
        /// <param name="count">Number of HEX encoded bytes to read. (1 HEX encoded byte will read out 2 bytes from memory stream)</param>
        /// <returns>Byte array</returns>

        public byte[] ReadHexEncoded(int count)
        {
            return Common.DecodeHex(ReadString(count << 1));
        }

        /// <summary>
        /// Reads bytes from memory stream to given buffer up to the length of buffer
        /// </summary>
        /// <param name="buffer">Buffer to be filled with bytes from memory stream</param>
        /// <returns>Number of bytes readed</returns>
        public int Read(byte[] buffer)
        {
            return Read(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// Reads string (ASCII) from memory stream
        /// </summary>
        /// <param name="count">Number of bytes to read</param>
        /// <returns>ASCII string</returns>
        public string ReadString(int count)
        {
            byte[] ba = new byte[count];
            int i = Read(ba, 0, ba.Length);
            return Encoding.ASCII.GetString(ba, 0, i);
        }
        private static char[] ca = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

        /// <summary>
        /// Reads BCD encoded string.
        /// </summary>
        /// <param name="count">Number of nibles to read. (2 nibbles = 1 byte in memory stream, 1 nibble = 1 char in string)</param>
        /// <returns>ASCII string</returns>
        public string ReadBcdString(int count)
        {
            byte[] ba = new byte[(count + (count & 1)) >> 1];
            int i = Read(ba, 0, ba.Length);
            char[] cc = new char[i << 1];
            for(int ck = 0; ck < i; ck++)
            {
                cc[(ck << 1)] = ca[(ba[ck] >> 4)];
                cc[(ck << 1) + 1] = ca[(ba[ck] & 0x0F)];
            }
            return new string(cc, count & 1, count);
        }
        /// <summary>
        /// Reads HEX encoded byte array.
        /// </summary>
        /// <param name="count">Number of HEX encoded bytes to read. (1 HEX encoded byte will read out 2 bytes from memory stream)</param>
        /// <returns>Byte array</returns>
        public byte[] ReadHextEncoded(int count)
        {
            return Common.DecodeHex(ReadString(count << 1));
        }
        #endregion

        #region Writing

        /// <summary>
        /// Fills memory stream with given value
        /// </summary>
        /// <param name="ch">Char to fill</param>
        /// <param name="count">Count of charachters to fill</param>
        public void Fill(char ch, int count)
        {
            byte[] bb = new byte[count];
            for(int i = 0; i < count; i++) bb[i] = (byte)ch;
            Write(bb);
        }
        /// <summary>
        /// Fills memory stream with given value
        /// </summary>
        /// <param name="b">Byte to fill</param>
        /// <param name="count">Count of bytes to fill</param>
        public void Fill(byte b, int count)
        {
            byte[] bb = new byte[count];
            for(int i = 0; i < count; i++) bb[i] = b;
            Write(bb);
        }

        /// <summary>
        /// Writes whole given buffer to memory stream
        /// </summary>
        /// <param name="buffer">Buffer to write</param>
        public void Write(byte[] buffer)
        {
            base.Write(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// Performs BCD encoding on a string and writes results to memory steam. Bcd encoding is case insensitive
        /// </summary>
        /// <param name="s">String to BCD encode and write</param>
        public void WriteBcdEncoded(string s)
        {
            int ps = s.Length & 1;
            byte b = 0;
            for(int ck = 0; ck < s.Length; ck++)
            {
                switch(char.ToUpper(s[ck]))
                {
                    default: throw new Exception("Invalid character for BCD encoding");
                    case '0': if(ps == 0) b = 0; else WriteByte(b); break;
                    case '1': if(ps == 0) b = 16; else WriteByte((byte)(b | 1)); break;
                    case '2': if(ps == 0) b = 32; else WriteByte((byte)(b | 2)); break;
                    case '3': if(ps == 0) b = 48; else WriteByte((byte)(b | 3)); break;
                    case '4': if(ps == 0) b = 64; else WriteByte((byte)(b | 4)); break;
                    case '5': if(ps == 0) b = 80; else WriteByte((byte)(b | 5)); break;
                    case '6': if(ps == 0) b = 96; else WriteByte((byte)(b | 6)); break;
                    case '7': if(ps == 0) b = 112; else WriteByte((byte)(b | 7)); break;
                    case '8': if(ps == 0) b = 128; else WriteByte((byte)(b | 8)); break;
                    case '9': if(ps == 0) b = 144; else WriteByte((byte)(b | 9)); break;
                    case 'A': if(ps == 0) b = 160; else WriteByte((byte)(b | 10)); break;
                    case 'B': if(ps == 0) b = 176; else WriteByte((byte)(b | 11)); break;
                    case 'C': if(ps == 0) b = 192; else WriteByte((byte)(b | 12)); break;
                    case 'D':
                    case '=': if(ps == 0) b = 208; else WriteByte((byte)(b | 13)); break;
                    case 'E': if(ps == 0) b = 224; else WriteByte((byte)(b | 14)); break;
                    case 'F': if(ps == 0) b = 240; else WriteByte((byte)(b | 15)); break;
                }
                ps ^= 1;
            }
        }

        /// <summary>
        /// Writes ASCII string to memory stream
        /// </summary>
        /// <param name="s">Value</param>
        public void WriteString(string s)
        {
            if(s == null || s.Length == 0)
                return;
            Write(Encoding.ASCII.GetBytes(s));
        }

        private static char[] HX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        /// <summary>
        /// Hex encodes byte array and writes result to memory stream
        /// </summary>
        /// <param name="buffer">Byte array to hex encode and write to memory stream</param>
        public void WriteHexEncoded(byte[] buffer)
        {
            if(buffer == null || buffer.Length == 0) return;
            byte[] ca = new byte[buffer.Length << 1];
            for(int ck = 0; ck < buffer.Length; ck++)
            {
                byte b = buffer[ck];
                ca[ck << 1] = (byte)HX[b >> 4];
                ca[(ck << 1) + 1] = (byte)HX[b & 0x0F];
            }
            Write(ca, 0, ca.Length);
        }

        /// <summary>
        /// Write Int32
        /// </summary>
        /// <param name="i">Value</param>
        public void WriteInt32(Int32 i)
        {
            Write(BitConverter.GetBytes(i), 0, 4);
        }
        /// <summary>
        /// Write Int16
        /// </summary>
        /// <param name="i">Value</param>
        public void WriteInt16(Int16 i)
        {
            Write(BitConverter.GetBytes(i), 0, 2);
        }

        /// <summary>
        /// Writes UInt32
        /// </summary>
        /// <param name="i">Value</param>
        public void WriteUInt32(UInt32 i)
        {
            WriteUInt32(i, false);
        }

        public void WriteUInt32(UInt32 i, bool usetNetworkByteOrder)
        {
            if(usetNetworkByteOrder)
            {
                byte[] ba = BitConverter.GetBytes(i);
                Log4cs.Log(Importance.Warning, "Using network byte order!!!");
                Array.Reverse(ba);
                Write(ba, 0, 4);
            } else
            {
                Write(BitConverter.GetBytes(i), 0, 4);
            }
        }
        #endregion

        /// <summary>
        /// Create memory stream
        /// </summary>
        /// <param name="b">Buffer containing initial data</param>
        /// <param name="start">Offset</param>
        /// <param name="length">Count</param>
        public MemoryStreamEx(byte[] b, int start, int length) : base(b, start, length) { }
        /// <summary>
        /// Create memory stream
        /// </summary>
        /// <param name="b">Buffer containing initial data</param>
        public MemoryStreamEx(byte[] b) : base(b) { }
        /// <summary>
        /// Create memory stream
        /// </summary>
        public MemoryStreamEx() : base() { }

        public static MemoryStreamEx operator ++(MemoryStreamEx ms)
        {
            ms.Seek(1, SeekOrigin.Current);
            return ms;
        }
    }
    #endregion



    #region " RegHelper - to get, delete and update values and keys in Registry "
    class RegHelper
    {

        public static object GetValue(string path, string name)
        {
            return GetValue(Registry.CurrentUser, path, name);
        }

        public static object GetValue(RegistryKey rootKey, string path, string name)
        {
            RegistryKey key = null;
            object value = "";

            try
            {
                //Log4cs.Log("Getting {0} from {1}", path + name, rootKey);
                key = rootKey.OpenSubKey(path);
                value = key.GetValue(name);
            } catch(Exception ex)
            {
                Log4cs.Log("Error getting " + name + " from " + path, Importance.Error);
                Log4cs.Log(ex.ToString(), Importance.Debug);
            }

            try
            {
                key.Close();
                rootKey.Close();
            } catch(Exception) {/* Log4cs.Log(ex.ToString()); */}

            return value;
        }

        /// <summary>
        /// Returns all values from registry path, using specified Registry key
        /// </summary>
        /// <param name="path">path to registry key beginning </param>
        /// <returns>Dictionary w/ values or empty</returns>
        public static Dictionary<string, object> GetAllValues(RegistryKey rootKey, string path)
        {
            Dictionary<string, object> arValues = new Dictionary<string, object>();
            //KeyValuePair<string, object>[] arValues1 = null;
            string[] arKeys = null;
            //string subPath = "";
            RegistryKey key = null;
            //RegistryKey rootKey = Registry.CurrentUser;

            if(path == null)
            {
                Log4cs.Log("No path specified for Registry!", Importance.Error);
                return arValues;
            }

            if(path.StartsWith("\\"))
                path = path.Substring(1);

            Log4cs.Log("Get values from: {0}\\{1}", rootKey.ToString(), path);

            try
            {
                key = rootKey.OpenSubKey(path);
                arKeys = key.GetValueNames();
                Log4cs.Log(Importance.Debug, "Got " + arKeys.Length + " values in {0}\\{1}", rootKey.ToString(), path);

                if(arKeys.Length > 0)
                {
                    for(int i = 0; i < arKeys.Length; i++)
                    {
                        try
                        {
                            arValues[arKeys[i]] = key.GetValue(arKeys[i]).ToString();
                        } catch(Exception)
                        {
                            Log4cs.Log(Importance.Warning, "Duplicate key [" + arKeys[i] + "]");
                        }

                        //Log4cs.Log("\t" + arKeys[i] + "->" + key.GetValue( arKeys[i] ).ToString() );
                    }
                }  // END IF

            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error listing " + rootKey.ToString() + "\\" + path);
                Log4cs.Log(Importance.Debug, ex.ToString());
                //return m_arValues;
            }

            try
            {
                key.Close();
                rootKey.Close();
            } catch(Exception) { }

            return arValues;
        }

        public static Dictionary<string, Dictionary<string, object>> GetAllSubKeysValues(RegistryKey rootKey, string path)
        {
            Dictionary<string, Dictionary<string, object>> arDict = new Dictionary<string, Dictionary<string, object>>();
            Dictionary<string, object> arValues = new Dictionary<string, object>();
            //string[] arKeys = null;
            //string subPath = "";
            RegistryKey key = null;

            if(path == null)
            {
                Log4cs.Log("No path specified for Registry!", Importance.Error);
                return null;
            }

            if(path.StartsWith("\\"))
                path = path.Substring(1);

            Log4cs.Log("Get subkeys from: {0}\\{1}", rootKey.ToString(), path);

            try
            {
                key = rootKey.OpenSubKey(path);
                string[] arSubKeys = key.GetSubKeyNames();
                for(int i = 0; (arSubKeys != null) && (i < arSubKeys.Length); i++)
                {
                    //Log4cs.Log(Importance.Debug, "\t" + arSubKeys[i]);
                    arDict[arSubKeys[i]] = RegHelper.GetAllValues(rootKey, path + "\\" + arSubKeys[i]);
                }

            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error listing " + rootKey.ToString() + "\\" + path);
                Log4cs.Log(Importance.Debug, ex.ToString());
                arDict = null;
            }

            try
            {
                key.Close();
                rootKey.Close();
            } catch(Exception) { }

            return arDict;
        }


        public static void Save(string path, string name, string value)
        {
            Save(Registry.CurrentUser, path, name, value);
        }

        public static void Save(RegistryKey rootKey, string path, string name, string value)
        {
            RegistryKey key = null;

            try
            {
                //Log4cs.Log("Getting {0} from {1}", path + name, rootKey);
                key = rootKey.CreateSubKey(path, RegistryKeyPermissionCheck.ReadWriteSubTree);
                key.SetValue(name, value);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error saving {0} in [{1}]{2}!", name, rootKey, path);
                Log4cs.Log(ex.ToString(), Importance.Debug);
            }

            try
            {
                key.Close();
                rootKey.Close();
            } catch(Exception) { }

        }

        public static void Save(RegistryKey rootKey, string path, string name, int value)
        {
            RegistryKey key = null;
            object obj = (object)value;

            try
            {
                //Log4cs.Log("Getting {0} from {1}", path + name, rootKey);
                key = rootKey.CreateSubKey(path, RegistryKeyPermissionCheck.ReadWriteSubTree);
                key.SetValue(name, value);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error saving {0} in [{1}]{2}!", name, rootKey, path);
                Log4cs.Log(ex.ToString(), Importance.Debug);
            }

            try
            {
                key.Close();
                rootKey.Close();
            } catch(Exception) { }

        }


        public static void Delete(RegistryKey rootKey, string path, string name)
        {
            RegistryKey key = null;

            try
            {
                //Log4cs.Log("Getting {0} from {1}", path + name, rootKey);
                key = rootKey.OpenSubKey(path, RegistryKeyPermissionCheck.ReadWriteSubTree);
                key.DeleteValue(name, true);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error deleting {0} in [{1}]{2}!", name, rootKey, path);
                Log4cs.Log(ex.ToString(), Importance.Debug);
            }

            try
            {
                key.Close();
                rootKey.Close();
            } catch(Exception) { }
        }
    }  // END CLASS RegHelper

    #endregion


    #region " Log4cs - class for logging "

    public enum Importance { Debug, Info, Warning, Error, No, HideThis };

    /// <summary>
    /// Class for simple logging
    /// </summary>
    public class Log4cs
    {

        protected static string m_fileName = "Log_{0}.log";
        protected static string m_logsDir = @"c:\logs\";

        protected static object m_monitor = "xx";

        /// <summary>
        /// Name of log file. Use {0} to place date in file name, i.e. logs_{0}.log
        /// </summary>
        public static string FileName
        {
            get { return m_fileName; }
            set
            {
                if(!value.Contains("{0}"))
                    throw new FormatException("Log filename must contains '{0}'!");

                m_fileName = value;
            }
        }

        /// <summary>
        /// Gets/sets directory where logs are
        /// </summary>
        public static string Dir
        {
            get { return m_logsDir; }
            set
            {
                m_logsDir = value;
                if(!m_logsDir.EndsWith("\\"))
                    m_logsDir += "\\";
            }
        }


        /// <summary>
        /// Time in log file, set to "" if no time is needed
        /// </summary>
        public static string TimeFmt = "HH:mm:ss";
        /// <summary>
        /// Do we need to output to console
        /// </summary>
        public static bool OutputToConsole = false;

        public static void Log(string msg, params object[] Args)
        {
            Log(Importance.Info, msg, Args);
        }


        public static void Log(Importance logLevel, string msg, params object[] Args)
        {
            DirectoryInfo dirInfo = new DirectoryInfo(Dir);
            StringBuilder sb = new StringBuilder();
            StringBuilder strMsg = new StringBuilder();
            string filename = "";
            StreamWriter streamW = null;

            if(!dirInfo.Exists)
            {
                try
                {
                    dirInfo.Create();
                } catch(Exception)
                {
                    System.Console.WriteLine("Error creating '{0}' directory!", Dir);
                    return;
                }
            }  // END IF

            try
            {
                sb.AppendFormat(FileName, DateTime.Now.ToString("yyyyMMdd"));
                filename = dirInfo.FullName;

                // Ensures that ends w/ '\'
                if(!filename.EndsWith("\\"))
                    filename += "\\";

                filename += sb.ToString();
                //            System.Console.WriteLine( "Log file name: " + filename );

            } catch(Exception)
            {
                System.Console.WriteLine("Bad log file name '{0}'!", FileName);
            }

            if(TimeFmt.Length > 0)
                strMsg.Append(DateTime.Now.ToString(TimeFmt)).Append(" ");

            if(logLevel != Importance.No)
            {
                if(logLevel == Importance.HideThis)
                {
                    strMsg.Append("[*** ").Append(logLevel.ToString().ToUpper()).Append(" ***] ");
                } else
                {
                    strMsg.Append("[").Append(logLevel.ToString().ToUpper()).Append("] ");
                }
            }

            //strMsg += " " + msg + Environment.NewLine;
            strMsg.AppendFormat(msg, Args).Append(Environment.NewLine);

            if(OutputToConsole)
                System.Console.Write(strMsg.ToString());

            try
            {
                lock(m_monitor)
                {
                    streamW = new StreamWriter(filename, true);
                    streamW.AutoFlush = true;
                    streamW.Write(strMsg);
                }
            } catch(Exception ex)
            {
                System.Console.WriteLine("Error writing to '{0}' file!", filename);
                System.Console.WriteLine(ex.ToString());
            }

            if(streamW != null)
                streamW.Close();
        }

        /*
         *  Log levels for events log 
         */
        private static System.Diagnostics.EventLogEntryType[] m_arLogLevels = 
        { 
            System.Diagnostics.EventLogEntryType.Information,
            System.Diagnostics.EventLogEntryType.Warning,
            System.Diagnostics.EventLogEntryType.Error 
        };


        /// <summary>
        /// Logs event in Windows application events
        /// </summary>
        public static void LogEvent(string title, string text, int level, int code)
        {
            try
            {
                if(!System.Diagnostics.EventLog.SourceExists(title))
                    System.Diagnostics.EventLog.CreateEventSource(title, "Application");
                System.Diagnostics.EventLog.WriteEntry(title, text, m_arLogLevels[level], code);
            } catch
            {

            }
        }

    }  // END CLASS Log4cs 
    #endregion

}
